/*
 * logger.cpp
 *
 *  Created on: 2008-9-4
 *      Author: fanliangliang
 */

#include "logger.h"
#include "appender.h"
#include "thread.h"
#include "foreach.h"
#include "bind.h"
#include "tss_ptr.h"

BEGIN_NAMESPACE_TOXIC

Logger::Logger()
:_level(LEVEL_DEBUG)
,_logger(0)
,_isAlive(true) {
	tssEvent();
	tssMessage();
	_logger = new Thread(toxic::bind(&Logger::doLogger, this));
	_logger->start();
}

Logger::~Logger() {
	if (_isAlive) {
		close();
	}
}

void Logger::addAppender(ostream& stream) {
	addAppender(new ConsoleAppender(stream));
}

void Logger::addAppender(const char* filename, ios_base::openmode mode /*= ios_base::out | ios_base::trunc*/) {
	addAppender(new FileAppender(filename, mode));
}

void Logger::addAppender(LogAppender* appender) {
	ScopedLock lock(_appendersMutex);
	_appenders.push_back(appender);
}

void Logger::setLevel(LogLevel level) {
	_level = level;
}

LogLevel Logger::level() const {
	return _level;
}

bool Logger::isEnabledFor(LogLevel level) const {
	return level >= _level;
}

void Logger::close() {
	_isAlive = false;
	_logger->join();
	delete _logger;
	_logger = 0;

	foreach (LogAppender* appender, _appenders) {
		delete appender;
	}
	_appenders.clear();
}

Logger* Logger::instance() {
	return Singleton<Logger>::instance();
}

Logger& Logger::setLogParameter(LogLevel level, const char* file, int line, const Sentry& sentry /*= Sentry()*/) {
	tssNeedEndl() = true;
	LogEvent& event = tssEvent();
	event.level = level;
	event.file = file;
	event.line = line;
	return *this;
}

void Logger::operator()(const char* format, ...) {
	tssNeedEndl() = false;
	va_list alist;
  	va_start(alist, format);
  	logFormat(format, alist);
	va_end(alist);
}

void Logger::logFormat(const char* format, va_list alist) {
	char message[4096] = { 0 };
	vsnprintf(message, sizeof(message), format, alist);
	addEvent(createEvent(message));
}

bool Logger::swapEvents(vector<LogEvent*>& events) {
	ScopedLock lock(_eventsMutex);
	if (_events.empty()) {
		return false;
	}
	else {
		events.swap(_events);
		return true;
	}
}

LogEvent* Logger::createEvent(const string& message) const {
	LogEvent* e = new LogEvent();
	LogEvent& event = tssEvent();
	e->tid = Thread::self();
	e->level = event.level;
	e->file = event.file;
	e->line = event.line;
	e->message = message;
	return e;
}

void Logger::addEvent(LogEvent* e) {
	ScopedLock lock(_eventsMutex);
	_events.push_back(e);
}

void Logger::flush() {
	vector<LogEvent*> events;
	if (swapEvents(events)) {
		foreach (LogEvent* e, events) {
			foreach (LogAppender* appender, _appenders) {
				appender->doAppend(*e);
			}
			delete e;
		}
	}
}

void Logger::endl() {
	bool& needEndl = tssNeedEndl();
	if (needEndl) {
		needEndl = false;
		ostringstream& oss = tssMessage();
		addEvent(createEvent(oss.str()));
		oss.str("");
	}
}

void Logger::doLogger() {
	while (_isAlive) {
		Thread::sleep(100);
		flush();
	}
	flush();
}

LogEvent& Logger::tssEvent() {
	static tss_ptr<LogEvent> event;
	return *event.safe_get();
}

ostringstream& Logger::tssMessage() {
	static tss_ptr<ostringstream> message;
	return *message.safe_get();
}

bool& Logger::tssNeedEndl() {
	static tss_ptr<bool> needEndl;
	return *needEndl.safe_get();
}

END_NAMESPACE_TOXIC
